package com.example.base_model.utils

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/**
 * @Author jiangKunKun
 * @CreateDate 2025/10/2
 * @Description 支持自定义延迟时间和外部发送逻辑的队列发送工具类。
 *              该工具类允许以受控的速率发送数据，并可配置发送延迟和实际发送逻辑。
 * @SpittingGrooves
 */
class QueueSendingDataUtils<T> {
    /**
     * 消息发送队列，使用 [Channel.UNLIMITED] 创建一个无限制容量的通道。
     * 生产者可以无限制地向其中发送消息，而不会被阻塞。
     */
    private val sendQueue = Channel<T>(Channel.UNLIMITED)

    /**
     * 负责从 [sendQueue] 中取出消息并以指定延迟发送的协程任务。
     * 如果没有正在进行的发送任务，则为 null。
     */
    private var sendJob: Job? = null

    /**
     * 协程作用域，用于启动和管理与队列发送相关的协程。
     * 使用 [Dispatchers.IO] 进行 I/O 操作，并结合 [SupervisorJob] 以确保子协程的失败不会取消父协程。
     */
    private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    /**
     * 消息发送之间的延迟时间（毫秒）。
     * 默认为 100 毫秒。
     */
    private var delayMillis: Long = 100

    /**
     * 实际执行消息发送的函数。
     * 由外部传入实现具体的发送逻辑。
     * 如果未设置，则为 null。
     */
    private var sendFunction: ((T) -> Unit)? = null

    /**
     * 初始化发送队列。
     * 设置消息发送的延迟时间和实际的发送回调函数，并启动一个协程来处理队列中的消息。
     *
     * @param delayMillis 消息发送之间的延迟时间(毫秒)，默认为 100ms。
     * @param sendFunction 实际发送消息的函数，由外部实现具体的发送逻辑。
     */
    fun initSendQueue(delayMillis: Long = 100, sendFunction: (T) -> Unit) {
        this.delayMillis = delayMillis
        this.sendFunction = sendFunction

        // 启动一个协程来持续从队列中取出消息并发送
        sendJob = coroutineScope.launch {
            for (entity in sendQueue) { // 遍历消息队列，当有新消息时会继续执行
                sendInternal(entity) // 调用内部发送方法
                delay(delayMillis) // 按照设定的延迟时间等待
            }
        }
    }

    /**
     * 将消息发送到队列中。
     * 此操作是非阻塞的，消息会被添加到队列尾部。
     *
     * @param msg 要发送的消息。
     */
    fun send(msg: T) {
        coroutineScope.launch { // 在协程作用域中启动一个新协程来发送消息
            sendQueue.trySend(msg) // 尝试将消息发送到通道，如果通道已关闭则返回失败但不会抛出异常
        }
    }

    /**
     * 清理资源。
     * 取消发送协程任务，关闭消息队列，并取消整个协程作用域，以释放所有相关资源。
     */
    fun close() {
        sendJob?.cancel() // 取消消息发送协程任务
        sendQueue.close() // 关闭消息队列，表示不再接受新的消息
        coroutineScope.coroutineContext[Job]?.cancel() // 取消整个作用域中的所有协程任务
    }

    /**
     * 内部实际发送方法。
     * 调用外部传入的 `sendFunction` 来处理消息。
     *
     * @param msg 待发送的消息。
     */
    private fun sendInternal(msg: T) {
        sendFunction?.invoke(msg) // 如果 sendFunction 不为 null，则调用它来处理消息
    }
}